home *** CD-ROM | disk | FTP | other *** search
/ Celestin Apprentice 2 / Apprentice-Release2.iso / Source Code / C / Utilities / byacc 1.8.2 / test / calc.y < prev    next >
Encoding:
Text File  |  1993-02-04  |  5.0 KB  |  257 lines  |  [TEXT/R*ch]

  1. /*
  2.     Simple calculator implemented in Perl.
  3.     The grammar is standard YACC, but the actions are in Perl.
  4.     This YACC file must be processed by a version of YACC that
  5.     supports Perl output.
  6.  
  7.     The calculator handles the basic operations, but nothing fancy.
  8.     This is mostly a demonstration of the Perl/Yacc combination.
  9.  
  10.     $P holds the previous value, that is, the value of the most
  11.        recent expression.  The user can refer to this with "%".
  12.     %V holds user-defined variables; variables are defined by assinging
  13.        to them.  References to an undefined variable produces a warning,
  14.        and assigns 0 to the variable.
  15.     The user can also get a Perl escape with &, followed by a string.
  16.  
  17.     Ray Lischner
  18.     17 August 1990
  19. */
  20.  
  21. %token INT FLOAT STRING IDENT
  22.  
  23. %left    '='
  24. %left    '|'
  25. %left    '&'
  26. %left    EQ NE
  27. %left    GT GE LT LE
  28. %left    L_SHIFT R_SHIFT
  29. %left    '+' '-'
  30. %left    '*' '/'
  31. %right    EXP
  32. %right    '!'
  33. %right    UNARY
  34.  
  35. %start    stmt_list
  36.  
  37. %%
  38.  
  39. stmt_list:    /* empty */
  40.     |    stmt_list stmt
  41.     ;
  42.  
  43. stmt:        terminator
  44.     |    expr terminator
  45.         { print $1, "\n" if $2 eq "\n"; $P = $1; }
  46.     |    error terminator
  47.         { &yyerrok; }
  48.     ;
  49.  
  50. terminator:    ';'
  51.         { $$ = $1; }
  52.     |    '\n'
  53.         { $$ = $1; }
  54.     ;
  55.  
  56. expr:        '(' expr ')'
  57.         { $$ = $2; }
  58.     |    expr '|' expr
  59.         { $$ = $1 || $3; }
  60.     |    expr '&' expr
  61.         { $$ = $1 && $3; }
  62.     |    expr EQ expr
  63.         { $$ = $1 == $3; }
  64.     |    expr NE expr
  65.         { $$ = $1 != $3; }
  66.     |    expr GT expr
  67.         { $$ = $1 > $3; }
  68.     |    expr GE expr
  69.         { $$ = $1 >= $3; }
  70.     |    expr LT expr
  71.         { $$ = $1 < $3; }
  72.     |    expr LE expr
  73.         { $$ = $1 <= $3; }
  74.     |    expr L_SHIFT expr
  75.         { $$ = $1 << $3; }
  76.     |    expr R_SHIFT expr
  77.         { $$ = $1 >> $3; }
  78.     |    expr '+' expr
  79.         { $$ = $1 + $3; }
  80.     |    expr '-' expr
  81.         { $$ = $1 - $3; }
  82.     |    expr '*' expr
  83.         { $$ = $1 * $3; }
  84.     |    expr '/' expr
  85.         { $$ = $1 / $3; }
  86.     |    expr EXP expr
  87.         { $$ = $1 ** $3; }
  88.     |    expr '!'
  89.         { $$ = &fact($1); }
  90.     |    '-' expr        %prec UNARY
  91.         { $$ = -$2; }
  92.     |    '!' expr        %prec UNARY
  93.         { $$ = !$2; }
  94.     |    '+' expr        %prec UNARY
  95.         { $$ = $2; }
  96.     |    '&' STRING        %prec UNARY
  97.         { $$ = eval($2); }
  98.     |    IDENT '=' expr
  99.         { eval '$V{'.$1.'}=('.$3.'); 1' || &yyerror($@); $$ = $V{$1}; }
  100.     |    IDENT
  101.         { if (! defined $V{$1}) {
  102.             &yyerror($1.": undefined variable");
  103.             $V{$1} = 0;
  104.           }
  105.           $$ = $V{$1};
  106.         }
  107.     |    INT
  108.         { $$ = $1; }
  109.     |    FLOAT
  110.         { $$ = $1; }
  111.     |    STRING
  112.         { $$ = $1; }
  113.     |    '%'
  114.         { $$ = $P; }
  115.     ;
  116.  
  117. %%
  118.  
  119. # Prompt the user on STDERR, but only prompt if STDERR and the input
  120. # file are both terminals.
  121.  
  122. # read from STDIN if no files are named on the command line
  123. unshift(@ARGV, '-') if $#ARGV < $[;
  124.  
  125. # After finishing a file, open the next one.  Return whether there
  126. # really is a next one that was opened.
  127. sub next_file
  128. {
  129.     while ($ARGV = shift(@ARGV)) {
  130.     if (! open(ARGV, $ARGV)) {
  131.         print STDERR "$ARGV: cannot open file: $!\n";
  132.         next;
  133.     }
  134.     $prompt = (-t ARGV && -t STDERR) ? '(Calc) ' : '';
  135.     last;
  136.     }
  137.     $ARGV >= $[;
  138. }
  139.  
  140. # print he prompt
  141. sub prompt
  142. {
  143.     print STDERR $prompt if $prompt;
  144. }
  145.  
  146. # print an error message
  147. sub yyerror
  148. {
  149.     print STDERR "\"$ARGV\", " if $ARGV ne '-';
  150.     print STDERR "line $.: ", @_, "\n";
  151. }
  152.  
  153. # Hand-coded lex until I write lex -p, too!
  154. sub yylex
  155. {
  156.  lexloop:
  157.     {
  158.     # get a line of input, if we need it.
  159.     if ($line eq '') {
  160.         &prompt;
  161.         $line = <ARGV>;
  162.         if ($line eq '') {
  163.         close(ARGV);
  164.         &next_file || return(0);
  165.         }
  166.     }
  167.  
  168.     # Skip over white space, and grab the first character.
  169.     # If there is no such character, then grab the next line.
  170.     $line =~ s/^[ \t\f\r\v]*(.|\n)//    || next lexloop;
  171.     local($char) = $1;
  172.     if ($char eq '#') {
  173.         # comment, so discard the line
  174.         $line = "\n";
  175.         &yylex;
  176.     } elsif ($char =~ /^['"]/) {
  177.         # collect the string
  178.         if ($line =~ s/^([^$char]*)$char//) {
  179.         $yylval = $1;
  180.         } else {
  181.         &yyerror('unterminated string');
  182.         $yylval = '';
  183.         }
  184.         $STRING;
  185.     } elsif ($char =~ /^\d/) {
  186.         # number, is it integer or float?
  187.         if ($line =~ s/^(\d+)//) {
  188.         $yylval = int($char . $1);
  189.         } else {
  190.         $yylval = int($char);
  191.         }
  192.         $type = $INT;
  193.         if ($line =~ s/^(\.\d*)//) {
  194.         $tmp = "0$1";
  195.         $yylval += $tmp;
  196.         $type = $FLOAT;
  197.         }
  198.         if ($line =~ s/^[eE]([-+]*\d+)//) {
  199.         $yylval *= 10 ** $1;
  200.         $type = $FLOAT;
  201.         }
  202.         $type;
  203.     } elsif ($char =~ /^\w/) {
  204.         # identifier
  205.         $line =~ s/^([\w\d]*)//;
  206.         $yylval = $char.$1;
  207.         $IDENT;
  208.     } elsif ($char eq '*' && $line =~ s/^\*//) {
  209.         $EXP;
  210.     } elsif ($char eq '!' && $line =~ s/^=//) {
  211.         $NE;
  212.     } elsif ($char eq '=' && $line =~ s/^=//) {
  213.         $EQ;
  214.     } elsif ($char =~ /^[<>]/ && $line =~ s/^=//) {
  215.         $char eq '<' ? $LE : $GE;
  216.     } elsif ($char =~ /^[<>]/ && $line =~ s/^$char//) {
  217.         $char eq '<' ? $L_SHIFT : $R_SHIFT;
  218.     } else {
  219.         $yylval = $char;
  220.         ord($char);
  221.     }
  222.     }
  223. }
  224.  
  225. # factorial
  226. sub fact
  227. {
  228.     local($n) = @_;
  229.     local($f) = 1;
  230.     $f *= $n-- while ($n > 1) ;
  231.     $f;
  232. }
  233.  
  234. # catch signals
  235. sub catch
  236. {
  237.     local($signum) = @_;
  238.     print STDERR "\n" if (-t STDERR && -t STDIN);
  239.     &yyerror("Floating point exception") if $signum = 8;
  240.     next outer;
  241. }
  242. $SIG{'INT'} = 'catch';
  243. $SIG{'FPE'} = 'catch';
  244.  
  245. select(STDERR); $| = 1;
  246. select(STDOUT);
  247. &next_file;
  248.  
  249. # main program
  250. outer: while(1)
  251. {
  252.     $line = '';
  253.     eval '$status = &yyparse;';
  254.     exit $status if ! $@;
  255.     &yyerror($@);
  256. }
  257.